Android — 事件分发机制(二)
前言
上一篇我们其实已经知道事件分发机制的大概内容了,也是参考了郭大叔的两篇博文总结的。但是隐约感觉有点乱,要是真自己说说事件分发机制,又似乎无从谈起。本篇文章则是侧重总结性,在参考一些较新的博文来慢慢阐述,与上一篇最大的不同的是,本篇还是很建议大家阅读的,虽然我很菜。
参考以下:
事件分发、拦截与消费
类型 | 相关方法 | Activity | ViewGroup | View |
---|---|---|---|---|
事件分发 | dispatchTouchEvent() | 有 | 有 | 有 |
事件拦截 | onInterceptTouchEvent() | 无 | 有 | 无 |
事件消费 | onTouchEvent() | 有 | 有 | 有 |
从上表可以看出,Activity和View都是没有事件拦截功能的,这是因为:
- Activity作为原始的事件分发者,如果Activity拦截了事件就会导致整个屏幕都无法相应事件,那么之前给控件注册事件就显得画蛇添足。
- View作为事件传递的最末端,要么消费掉事件,要么不处理进行回传,根本没必要进行事件拦截。
事件分发流程
事件收集之后最先传递给Activity,然后依次向下传递,大致如下:
|
|
如果最后分发给了View,View也没有处理事件会怎么办?那么事件就会反方向回传,最终传給Activity,如果最后Activity也没有处理,那么事件才会被抛弃:
|
|
这就是非常经典的责任链模式 ,如果我能处理就拦截下来自己干,如果处理不了就交给责任链中的下一个对象。
常见事件
先了解一下常见的几种事件,根据面向对象的思想,事件都被封装成了MotionEvent对象,本篇重点不在于此,所以只会涉及几个与手指触摸相关的常见事件:
时间 | 简介 |
---|---|
ACTION_DOWN | 手指 初次接触到屏幕 时触发 |
ACTION_MOVE | 手指 在屏幕上滑动 时触发,往往会触发多次 |
ACTION_UP | 手指 离开屏幕 时触发 |
ACTION_CANCEL | 时间 被上层拦截 时触发 |
事件分发、拦截与消费
类型 | 相关方法 | ViewGroup | View |
---|---|---|---|
事件分发 | dispatchTouchEvent() | 有 | 有 |
事件拦截 | onInterceptTouchEvent() | 有 | 无 |
事件消费 | onTouchEvent() | 有 | 有 |
dispatchTouchEvent() 是事件分发机制的核心,所有的事件调度都归他管。ViewGroup有很多子View需要管理,需要事件分发也说得过去,但是为什么View也要有事件分发呢?
那是因为View可以注册很多事件监听器:onClick()、onLongClick()、onTouch(),并且View自身也有onTouchEvent()方法,那么多事件方法肯定要有dispatchTouchEvent()来管理咯。
事件调度顺序:
ViewGroup相关
ViewGroup中可能有多个ChildView,如何判断应该分配给哪一个呢?
把所有ChildView遍历一遍,如果手指触摸点在ChildView区域内就分给这个View。
当该点的ChildView有重叠时应该如何分配呢?
当ChildView重叠时,一般会分配给显示在最上面的ChildView。那如何判断哪个是显示在最上面的呢?后面加载的一般会覆盖掉之前的,所以显示在最上面的是最后加载的。
ViewGroup和ChildView同时注册了事件监听器,哪个会先执行?
事件优先给ChildView,会被ChildView消费掉,ViewGroup不会相应。
所有事件都应该被同一View消费。
同一次点击事件只能被一个View消费。View中onClick事件需要同时接受到ACTION_DOWN和ACTION_UP才能触发,如果分配给了不同的View,那么onClick将无法被正确触发。
Android为了保证所有的事件都是被一个View消费的,对第一次事件(ACTION_DOWN)进行了特殊判断,View只有消费了ACTION_DOWN事件,才能接受到后续的事件(可点击控件默认会消费所有事件),并且会将后续所有事件传递过来,不会再传递给其他View,除非上层View进行了拦截。如果上层View拦截了当前正在处理的事件,会收到一个ACTION_CANCEL,表示当前事件已经结束,后续事件不会再传递过来。
一次触摸流程中产生事件应被同一个View消费,全部接受或者全部拒绝。